package com.nineshadow.games.netty.websocket;

import com.nineshadow.games.launch.HallServer;
import com.nineshadow.games.netty.message.MessageSend;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;

import java.io.FileOutputStream;

@Slf4j
public class EchoWebSocketServerHandler extends SimpleChannelInboundHandler<Object> {


    FileOutputStream outputStream;
    // 失败计数器：未收到client端发送的ping请求
    private int unRecPingTimes = 0;
    private WebSocketServerHandshaker handshaker;

    /**
     * response
     *
     * @param ctx
     * @param request
     * @param response
     */
    private static void sendHttpResponse(ChannelHandlerContext ctx,
                                         FullHttpRequest request, FullHttpResponse response) {
        //返回给客户端
        if (response.status().code() != HttpResponseStatus.OK.code()) {
            ByteBuf buf = Unpooled.copiedBuffer(response.status().toString(), CharsetUtil.UTF_8);
            response.content().writeBytes(buf);
            buf.release();
        }
        //如果不是keepalive那么就关闭连接
        ChannelFuture f = ctx.channel().writeAndFlush(response);
        if (response.status().code() != HttpResponseStatus.OK.code()) {
            f.addListener(ChannelFutureListener.CLOSE);
        }
    }

    /**
     * channel 通道 action 活跃的 当客户端主动链接服务端的链接后，这个通道就是活跃的了。也就是客户端与服务端建立了通信通道并且可以传输数据
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info("客户端与服务端连接开启：" + ctx.channel().remoteAddress().toString());
    }

    /**
     * channel 通道 Inactive 不活跃的 当客户端主动断开服务端的链接后，这个通道就是不活跃的。也就是说客户端与服务端关闭了通信通道并且不可以传输数据
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.info("客户端与服务端连接关闭：{}", ctx.channel().remoteAddress().toString());
        try {

            log.info("关闭 sessionId:{}", ctx.channel().id().asLongText());
            HallServer.offline(ctx);
        } catch (Exception e) {
            log.error("断线操作异常", e);
        }
        // 关闭，等待重连
        ctx.channel().close();
    }

    /**
     * exception 异常 Caught 抓住 抓住异常，当发生异常的时候，可以做一些相应的处理，比如打印日志、关闭链接
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.info("异常 sessionId:" + ctx.channel().id().asLongText());
        log.error("客户端与服务端异常：", cause);
        // ctx.channel().close();
    }

    /**
     * 接收客户端发送的消息 channel 通道 Read 读 简而言之就是从通道中读取数据，也就是服务端接收客户端发来的数据。但是这个数据在不进行解码时它是ByteBuf类型的
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 传统的HTTP接入 握手操作
        if (msg instanceof FullHttpRequest) {
            handleHttpRequest(ctx, ((FullHttpRequest) msg));
            // WebSocket接入
        } else if (msg instanceof WebSocketFrame) {
            //log.info(handshaker.uri());
            handlerWebSocketFrame(ctx, (WebSocketFrame) msg);
        }
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        //log.info("客户端与服务端空闲状态" +ctx.channel().remoteAddress().toString());
        if (evt instanceof IdleStateEvent) {
            unRecPingTimes = MessageSend.TimeIdleStateEvent(ctx, evt, unRecPingTimes);
        } else {
            super.userEventTriggered(ctx, evt);
        }
    }

    /**
     * channel 通道 Read 读取 Complete 完成 在通道读取完成后会在这个方法里通知，对应可以做刷新操作 ctx.flush()
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

    private void handlerWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {
        //log.info("数据");
        // 判断是否关闭链路的指令
        if (frame instanceof CloseWebSocketFrame) {
            System.out.println(1);
            handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
            return;
        }

        // 判断是否ping消息
        if (frame instanceof PingWebSocketFrame) {
            ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
            return;
        }

        //支持二进制消息
        if (frame instanceof BinaryWebSocketFrame) {
            log.info("支持二进制消息");
            BinaryWebSocketFrame img = (BinaryWebSocketFrame) frame.copy();
            ByteBuf byteBuf = img.content();
            outputStream = new FileOutputStream("D:\\a.jpg");
            byteBuf.readBytes(outputStream, byteBuf.capacity());
            byteBuf.clear();
            return;
        }

        if (frame instanceof ContinuationWebSocketFrame) {
            log.info("支持二进制继续消息");
            ContinuationWebSocketFrame img = (ContinuationWebSocketFrame) frame.copy();
            ByteBuf byteBuf = img.content();
            byteBuf.readBytes(outputStream, byteBuf.capacity());
            byteBuf.clear();
            log.info("支持二进制继续消息长度:");
        }

        if (frame instanceof TextWebSocketFrame) {
            // 返回应答消息
            String request = ((TextWebSocketFrame) frame).text();
            MessageSend.sendMessage(ctx, request);
        }
    }

    /**
     * 握手操作 第一次请求是http请求，请求头包括ws的信息
     *
     * @param ctx
     * @param req
     */
    public void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) {
        //如果http解码失败 则返回http异常 并且判断消息头有没有包含Upgrade字段(协议升级)
        if (!req.decoderResult().isSuccess()
                || (!"websocket".equals(req.headers().get("Upgrade")))) {
            sendHttpResponse(ctx, req, new DefaultFullHttpResponse(
                    HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
            return;
        }

        WebSocketServerHandshakerFactory wsFactory;
        if (WebSocketServer.iswss) {
            wsFactory = new WebSocketServerHandshakerFactory("wss:/" + ctx.channel() + "/websocket", null, false, 1048576);
        } else {
            wsFactory = new WebSocketServerHandshakerFactory("ws:/" + ctx.channel() + "/websocket", null, false, 1048576);
        }

        handshaker = wsFactory.newHandshaker(req);
        if (handshaker == null) {
            //不支持
            WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
        } else {
            handshaker.handshake(ctx.channel(), req);
        }
    }

}
